package edu.csu.smartpark.service.Impl;

import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import edu.csu.smartpark.model.DO.BillTypeDO;
import edu.csu.smartpark.model.DTO.BillDTO;
import edu.csu.smartpark.model.DTO.BillQueryDTO;
import edu.csu.smartpark.model.PO.BillInfo;
import edu.csu.smartpark.model.PO.BillTypeSetting;
import edu.csu.smartpark.model.VO.BillInfoVO;
import edu.csu.smartpark.model.common.BusinessException;
import edu.csu.smartpark.service.BillService;
import edu.csu.smartpark.util.CommonUtil;
import edu.csu.smartpark.util.MongoDBPageable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.*;

@Service
@Slf4j
public class BillServiceImpl implements BillService {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public String addBill(BillDTO billDTO) {
        BillInfo billInfo = new BillInfo();
        BeanUtils.copyProperties(billDTO, billInfo);
        String billNum = System.currentTimeMillis() + CommonUtil.generateRandom();
        Date now = CommonUtil.getCurrentTime();
        billInfo.setBillNumber(billNum);
        billInfo.setUpdateTime(now);
        billInfo.setCreateTime(now);
        mongoTemplate.save(billInfo);
        return billInfo.getId();
    }

    @Override
    public int removeBill(String billId) {
        Query query = new Query(Criteria.where("_id").is(billId));
        DeleteResult result = mongoTemplate.remove(query, BillInfo.class);
        if (result.getDeletedCount() <= 0){
            return -1;
        }
        return 1;
    }

    @Override
    public Map<String, Object> getBillList(BillQueryDTO billQueryDTO) {
        Query query = new Query();
        if (billQueryDTO.getBillNumber() != null && !billQueryDTO.getBillNumber().equals("")){
            query.addCriteria(Criteria.where("billNumber").is(billQueryDTO.getBillNumber()));
        }
        if (billQueryDTO.getEnterpriseId() != null && !billQueryDTO.getEnterpriseId().equals("")){
            query.addCriteria(Criteria.where("enterpriseId").is(billQueryDTO.getEnterpriseId()));
        }
        if (billQueryDTO.getEnterpriseName() != null && !billQueryDTO.getEnterpriseName().equals("")){
            query.addCriteria(Criteria.where("enterpriseName").is(billQueryDTO.getEnterpriseName()));
        }
        if (billQueryDTO.getStartDate() != null || billQueryDTO.getEndDate() != null){
            Criteria dateCriteria = new Criteria();
            if (billQueryDTO.getStartDate() != null && billQueryDTO.getEndDate() != null){
                dateCriteria.and("chargeDate").gte(billQueryDTO.getStartDate()).lte(billQueryDTO.getEndDate());
            } else if (billQueryDTO.getStartDate() == null && billQueryDTO.getEndDate() != null){
                dateCriteria.and("chargeDate").lte(billQueryDTO.getEndDate());
            }else if (billQueryDTO.getStartDate() != null && billQueryDTO.getEndDate() == null){
                dateCriteria.and("chargeDate").gte(billQueryDTO.getStartDate());
            }
            query.addCriteria(dateCriteria);
        }
        MongoDBPageable pageable = new MongoDBPageable();
        if (billQueryDTO.getPageNum() >= 0){
            pageable.setCurrentPage(billQueryDTO.getPageNum());
        }
        if (billQueryDTO.getPageSize() >= 0){
            pageable.setPageSize(billQueryDTO.getPageSize());
        }

        long total = mongoTemplate.count(query, BillInfo.class);
        Sort sort = Sort.by(Sort.Direction.DESC,"createTime");
        pageable.setSort(sort);
        query.with(pageable);
        List<BillInfo> billInfos = mongoTemplate.find(query,BillInfo.class);
        List<BillInfoVO> bills = new LinkedList<>();
        Map<String, Object> result = new HashMap<>();
        for (BillInfo billInfo : billInfos){
            BillInfoVO bill = new BillInfoVO();
            BeanUtils.copyProperties(billInfo, bill);
            bills.add(bill);
        }
        result.put("total", total);
        result.put("pageSize", pageable.getPageSize());
        result.put("pageNum", pageable.getPageNumber());
        result.put("data", bills);
        return result;
    }

    @Override
    public int addBillType(String parkId, String name) throws BusinessException{
        BillTypeDO billTypeDO = new BillTypeDO();
        Date currentTime = CommonUtil.getCurrentTime();
        String id = System.currentTimeMillis() + CommonUtil.generateRandom();
        billTypeDO.setId(id);
        billTypeDO.setName(name);
        billTypeDO.setCreateTime(currentTime);
        billTypeDO.setUpdateTime(currentTime);
        Query query = new Query(Criteria.where("parkId").is(parkId));
        BillTypeSetting setting = mongoTemplate.findOne(query, BillTypeSetting.class);
        if (setting == null){
            // 此园区没有配置过账单类型，需要插入一条新的记录
            setting = new BillTypeSetting();
            setting.setParkId(parkId);
            List<BillTypeDO> billTypes = new LinkedList<>();
            billTypes.add(billTypeDO);
            setting.setBillTypes(billTypes);
            BillTypeSetting insertResult = mongoTemplate.insert(setting);
            if (insertResult == null) {
                log.warn("[mongodb] insert billTypeSetting {} info fail",setting.getParkId());
                throw new BusinessException(BusinessException.SERVERERROR, "[mongodb] insert billTypeSetting {} info fail");
            }
        }else {
            List<BillTypeDO> billTypes = setting.getBillTypes();
            if (billTypes == null){
                billTypes = new LinkedList<>();
            }
            billTypes.add(billTypeDO);
            Update update = new Update();
            update.set("billTypes", billTypes);
            UpdateResult updateResult = mongoTemplate.updateFirst(query, update, BillTypeSetting.class);
            if (updateResult == null){
                log.warn("[mongodb] update billTypeSetting {} info fail",setting.getParkId());
                throw new BusinessException(BusinessException.SERVERERROR, "[mongodb] update billTypeSetting {} info fail");
            }
        }
        return 1;
    }

    @Override
    public int editBillType(String parkId, String typeId, String newTypeName) throws BusinessException {
        Query query = new Query(Criteria.where("parkId").is(parkId));
        BillTypeSetting setting = mongoTemplate.findOne(query, BillTypeSetting.class);
        if (setting == null){
            log.warn("park setting is not exist");
            throw new BusinessException(BusinessException.PARAMETERERROR, "park setting is not exist");
        }
        List<BillTypeDO> billTypes = setting.getBillTypes();
        if (billTypes == null){
            log.warn("bill type setting is not exist");
            throw new BusinessException(BusinessException.PARAMETERERROR, "bill type setting is not exist");
        }
        for (BillTypeDO billTypeDO : billTypes){
            if (billTypeDO.getId().equals(typeId)){
                billTypeDO.setName(newTypeName);
                break;
            }
        }
        Update update = new Update();
        update.set("billTypes", billTypes);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, BillTypeSetting.class);
        if (updateResult == null){
            log.warn("[mongodb] update billTypeSetting {} info fail",setting.getParkId());
            throw new BusinessException(BusinessException.SERVERERROR, "[mongodb] update billTypeSetting {} info fail");
        }
        return 1;
    }

    @Override
    public int deleteBillType(String parkId, String typeId) {
        Query query = new Query(Criteria.where("parkId").is(parkId));
        BillTypeSetting setting = mongoTemplate.findOne(query, BillTypeSetting.class);
        if (setting == null){
            log.warn("park setting is not exist");
            throw new BusinessException(BusinessException.PARAMETERERROR, "park setting is not exist");
        }
        List<BillTypeDO> billTypes = setting.getBillTypes();
        if (billTypes == null){
            log.warn("bill type setting is not exist");
            throw new BusinessException(BusinessException.PARAMETERERROR, "bill type setting is not exist");
        }
        for (BillTypeDO billTypeDO : billTypes){
            if (billTypeDO.getId().equals(typeId)){
                billTypes.remove(billTypeDO);
                break;
            }
        }
        Update update = new Update();
        update.set("billTypes", billTypes);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, BillTypeSetting.class);
        if (updateResult == null){
            log.warn("[mongodb] update billTypeSetting {} info fail",setting.getParkId());
            throw new BusinessException(BusinessException.SERVERERROR, "[mongodb] update billTypeSetting {} info fail");
        }
        return 1;
    }

    @Override
    public BillTypeSetting getBillTypes(String parkId) {
        Query query = new Query(Criteria.where("parkId").is(parkId));
        BillTypeSetting setting = mongoTemplate.findOne(query, BillTypeSetting.class);
        if (setting == null){
            log.warn("park setting is not exist");
            throw new BusinessException(BusinessException.PARAMETERERROR, "park setting is not exist");
        }
        return setting;
    }
}
